/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simulation;

import java.util.ArrayList;
import java.util.List;


/**
 *
 * @author Danielondon
 */
public class DrivingPattern implements IDrivingPattern{
    private int slotSize; // In minutes
    private int slotCount;
    private int [] isConnected;
    private double []kilometers;
    private int chargingTimes = -1;
    private double [] energyRequired;
    private final String name;
    public DrivingPattern(String name, int[] connected, double[] kilometersRunning, int slotCount, int slotSize) throws Exception {
        this.name = name;
        if (slotCount != connected.length || slotCount != kilometersRunning.length) {
            throw new Exception("Invalid slots were created for the driving pattern");
        }
        this.isConnected = connected;
        this.kilometers = kilometersRunning;
        this.slotCount = slotCount;
        this.slotSize = slotSize;
    }

    public DrivingPattern(String name, int[] connected, int slotCount, double[] energyRequired, int slotSize) throws Exception {
        this.name = name;
        if (slotCount != connected.length) {
            throw new Exception("Invalid slots were created for the driving pattern");
        }
        this.isConnected = connected;
        this.slotCount = slotCount;
        this.chargingTimes = energyRequired.length;
        this.energyRequired = energyRequired;
        this.slotSize = slotSize;
    }
    
    boolean isSlotValid(int slot) {
        return slot < getSlotCount();
    }
    
    public CarState getCarStatus(int slot){
        return CarState.fromInteger(this.getIsConnected(slot));
    }

    /**
     * @return the slotCount
     */
    public int getSlotCount() {
        return slotCount;
    }

    /**
     * @return the isConnected
     */
    @Override
    public int getIsConnected(int index) {
        return isConnected[index];
    }

    /**
     * @return the kilometers
     */
    @Override
    public double getKilometers(int index) {
        if (kilometers == null) {
            return 0;
        }
        return kilometers[index];
    }        

    @Override
    public int[] getConnected() {
        return isConnected;
    }
    
    @Override
    public double getConsumptionPerSlot(int index, ICarSpecs specs){
        double timeScale = (double)this.slotSize / (double)CommonResources.DEFAULT_INTERVAL;
        return (1 - this.isConnected[index])*
                this.kilometers[index]*
                specs.getPowerConsumptionPerKilometer()*
                timeScale*
                specs.getDischargingEfficiency();
    }
    
    @Override
    public double[] getConsumptionBlocks(ICarSpecs specs){
        if (this.energyRequired == null){
            this.calculateEnergyRequired(specs);
        }
        return this.energyRequired;
    }
    /*
     private int getNextIndex(int currentIndex){
        int nextIndex = currentIndex + 1;
        if (nextIndex == this.slotCount)
            nextIndex = 0;
        return nextIndex;
    }

    private int getPreviousIndex(int currentIndex){
        int prevIndex = currentIndex - 1;
        if (prevIndex == -1)
            prevIndex = this.slotCount - 1;
        return prevIndex;
    }*/

    @Override
    public int getChargingTimes() {
        if (this.chargingTimes == -1) {
            this.chargingTimes = 1;
            int currentState = isConnected[0];
            int previousState = currentState;
            for (int i = 1; i < this.slotCount; i++){
                currentState = isConnected[i];
                if (currentState != previousState){
                    chargingTimes ++;
                }
                previousState = currentState;
            }

            // handling last case for a loop
            if (this.isConnected[this.slotCount -1] == this.isConnected[0]){
                if (this.chargingTimes > 1){
                    this.chargingTimes --;
                }    
            }
            
            this.chargingTimes = this.chargingTimes / 2;
        }
        return this.chargingTimes;
    }
    
    @Override
    public List<int []> getConnectionsByRow(){
        int times = this.getChargingTimes();
        int currentState, previousState;
        List<int[]> patterns = new ArrayList();
        for (int k = 0;  k < times; k++) {
            int connectedFounded = 0;
            int []pattern = new int[this.slotCount];
            boolean firstDisconnectedFounded = false;
            boolean connected = false;
            previousState = -1;
            for (int i = 0; i < this.slotCount; i++){
                currentState = this.isConnected[i];
                if (currentState == CommonResources.DISCONNECTED){
                    firstDisconnectedFounded = true;
                }
                
                if (firstDisconnectedFounded) {
                    if (currentState == CommonResources.CONNECTED && previousState != currentState){
                        connectedFounded++;
                    }
                }                       

                // If the times that has been connected are equal to the 
                // one we are looking for
                if (connectedFounded == (k+1)){
                    connected =true;
                }

                if (connected && currentState == CommonResources.DISCONNECTED) {
                    connected = false;
                }

                if (connected){
                    pattern[i] = CommonResources.CONNECTED;
                } else {
                    pattern[i] = CommonResources.DISCONNECTED;
                }
                
                previousState = currentState;
            }

            if (connected){
                // REview the loop of the pattern, in case the end matchs the start
                for (int i = 0; i < this.slotCount; i++){
                    currentState = isConnected[i];
                    if (currentState == CommonResources.DISCONNECTED) {
                        break;
                    }
                    pattern[i] = currentState;                
                }
            }
                
            patterns.add(pattern);
        }
                
       return patterns;
    }

    private void calculateEnergyRequired(ICarSpecs specs) {
        List<Double> list = new ArrayList();
        int currentState = this.isConnected[0];
        int previousState;
        int indexList = 0;
        if (currentState == CommonResources.DISCONNECTED){
            list.add(new Double(getConsumptionPerSlot(0, specs)));
        }
        previousState = currentState;
        for (int i = 1; i < this.slotCount; i++){
            currentState = this.isConnected[i];
            if (currentState == CommonResources.DISCONNECTED) {
                if (currentState != previousState){
                    list.add(new Double(getConsumptionPerSlot(i, specs)));
                    indexList = list.size() - 1;
                } else {
                    double currentValue = list.get(indexList); 
                    list.set(indexList, new Double(currentValue + getConsumptionPerSlot(i, specs)));
                }
            }
            previousState = currentState;
        }
        
        // Merge in case first and last are the same
        if (this.isConnected[0] == this.isConnected[this.slotCount - 1] && this.isConnected[0] == CommonResources.DISCONNECTED){
            Double value = list.remove(list.size() - 1);
            Double first = list.get(0);
            first += value;
            list.set(0, first);
        }
        
        this.energyRequired = new double[list.size()];
        for (int i =0; i < this.energyRequired.length; i++) {
            this.energyRequired[i] = list.get(i);
        }
    }

    @Override
    public String getName() {
        return this.name;
    }
}
